/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.ListInitRequestMessage;
import cn.easyplatform.messages.response.ListInitResponseMessage;
import cn.easyplatform.messages.vos.ListInitVo;
import cn.easyplatform.messages.vos.datalist.ListHeaderVo;
import cn.easyplatform.messages.vos.datalist.ListVo;
import cn.easyplatform.spi.service.ListService;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Destroyable;
import cn.easyplatform.web.ext.Widget;
import cn.easyplatform.web.ext.ZkExt;
import cn.easyplatform.web.ext.zul.PivottableExt;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.support.ManagedComponent;
import cn.easyplatform.web.task.support.scripting.RhinoScriptable;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.zkoss.pivot.*;
import org.zkoss.pivot.impl.StandardCalculator;
import org.zkoss.pivot.impl.TabularPivotField;
import org.zkoss.pivot.impl.TabularPivotModel;
import org.zkoss.pivot.ui.PivotFieldControl;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;

import java.text.DecimalFormat;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class PivottableBuilder implements ComponentBuilder, ManagedComponent,
        Widget, PivotRendererExt, Destroyable {

    private OperableHandler main;

    private PivottableExt pivot;

    private TabularPivotModel model;

    private Map<String, String> formats;

    private RhinoScriptable scope;

    private Script scriptEval;

    public PivottableBuilder(OperableHandler main, ZkExt pivot) {
        this.main = main;
        this.pivot = (PivottableExt) pivot;
    }

    @Override
    public Component build() {
        if (Strings.isBlank(pivot.getEntity()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<pivottable>", "entity");
        pivot.setAttribute("$proxy", this);
        _fnf = new DecimalFormat("##,###.00");
        _nnf = new DecimalFormat("##,###");
        formats = new HashMap<>();
        if (!Strings.isBlank(pivot.getCellStyle())) {
            Context cx = Context.enter();
            try {
                cx.setOptimizationLevel(9);
                Map<String, Object> map = new HashMap<String, Object>();
                scope = new RhinoScriptable(map, false);
                scope.initStandardObjects(cx, false);
                scriptEval = cx.compileString(pivot.getCellStyle(), "", 1, null);
            } catch (RhinoException ex) {
                throw new EasyPlatformWithLabelKeyException(
                        "component.property.not.found", "<pivottable>", "cellStyle");
            } finally {
                Context.exit();
            }
        }
        if (pivot.isImmediate())
            redraw();
        return pivot;
    }

    private void redraw() {
        ListInitVo iv = new ListInitVo();
        iv.setId(pivot.getId());
        iv.setEntityId(pivot.getEntity());
        iv.setType(Constants.REPORT);
        iv.setImmediate(pivot.isImmediate());
        iv.setFilter(pivot.getFilter());
        iv.setShowPanel(false);
        ListService dls = ServiceLocator
                .lookup(ListService.class);
        IResponseMessage<?> resp = dls.doInit(new ListInitRequestMessage(
                main.getId(), iv));
        if (!resp.isSuccess())
            throw new BackendException(resp);
        ListVo lv = ((ListInitResponseMessage) resp).getBody();
        if (lv.getMatrixColumns() == null || lv.getMatrixColumns().length == 0)
            throw new EasyPlatformWithLabelKeyException("pivot.columns.empty", pivot.getEntity());
        if (lv.getMatrixRows() == null || lv.getMatrixRows().length == 0)
            throw new EasyPlatformWithLabelKeyException("pivot.rows.empty", pivot.getEntity());
        final List<List<Object>> data = new ArrayList<List<Object>>();
        final List<String> columns = new ArrayList<>();
        for (final ListHeaderVo hv : lv.getHeaders())
            columns.add(hv.getName());
        for (ListRowVo rv : lv.getData())
            data.add(Arrays.asList(rv.getData()));
        model = new TabularPivotModel(data, columns);
        for (final ListHeaderVo hv : lv.getHeaders()) {
            TabularPivotField pvf = model.getField(hv.getName());
            pvf.setTitle(hv.getTitle());
            if (ArrayUtils.contains(lv.getMatrixColumns(), hv.getName())) {//维度列
                model.setFieldType(pvf, PivotField.Type.COLUMN);
                if (hv.getTotalType() > 0)
                    model.setFieldSubtotals(pvf, new Calculator[]{getCalculator(hv.getTotalType())});
                if (!Strings.isBlank(hv.getFormat())) {
                    pvf.setGroupHandler(
                            new GroupHandler() {
                                @Override
                                public Object getGroup(Object obj) {
                                    if (obj instanceof Date) {
                                        return FastDateFormat.getInstance(hv.getFormat()).format((Date) obj);
                                    } else if (obj instanceof Number) {
                                        DecimalFormat format = new DecimalFormat(
                                                hv.getFormat());
                                        return format.format(obj);
                                    }
                                    return obj;
                                }
                            });
                }
                if (hv.isSort())
                    model.setFieldKeyOrder(pvf, true);
            } else if (ArrayUtils.contains(lv.getMatrixRows(), hv.getName())) {//布局列
                model.setFieldType(pvf, PivotField.Type.ROW);
                if (hv.getTotalType() > 0)
                    model.setFieldSubtotals(pvf, new Calculator[]{getCalculator(hv.getTotalType())});
                if (!Strings.isBlank(hv.getFormat())) {
                    pvf.setGroupHandler(
                            new GroupHandler() {
                                @Override
                                public Object getGroup(Object obj) {
                                    if (obj instanceof Date) {
                                        return FastDateFormat.getInstance(hv.getFormat()).format((Date) obj);
                                    } else if (obj instanceof Number) {
                                        DecimalFormat format = new DecimalFormat(
                                                hv.getFormat());
                                        return format.format(obj);
                                    }
                                    return obj;
                                }
                            });
                }
                if (hv.isSort())
                    model.setFieldKeyOrder(pvf, true);
            } else {//数据列
                if (hv.getType() == FieldType.INT || hv.getType() == FieldType.NUMERIC || hv.getType() == FieldType.LONG) {
                    model.setFieldType(pvf, PivotField.Type.DATA);
                    if (hv.getTotalType() > 0)
                        model.setFieldSummary(pvf, getCalculator(hv.getTotalType()));
                } else
                    model.setFieldType(pvf, PivotField.Type.UNUSED);
            }
            if (!Strings.isBlank(hv.getFormat()))
                formats.put(hv.getName(), hv.getFormat());
        }
        pivot.setModel(model);
        pivot.setPivotRenderer(this);
        if (!Strings.isBlank(pivot.getControl())) {
            PivotFieldControl pfc = (PivotFieldControl) pivot
                    .getFellowIfAny(pivot.getControl());
            if (pfc != null)
                pfc.setModel(model);
        }
    }

    private StandardCalculator getCalculator(int type) {
        if (type == 1)
            return StandardCalculator.SUM;
        if (type == 3)
            return StandardCalculator.COUNT;
        if (type == 2)
            return StandardCalculator.AVERAGE;
        if (type == 4)
            return StandardCalculator.MAX;
        if (type == 5)
            return StandardCalculator.MIN;
        if (type == 6)
            return StandardCalculator.SUM_OR_COUNT;
        if (type == 7)
            return StandardCalculator.COUNT_NUMBER;
        if (type == 8)
            return StandardCalculator.PRODUCT;
        if (type == 9)
            return StandardCalculator.STD_DEV;
        if (type == 10)
            return StandardCalculator.STD_DEV_P;
        if (type == 11)
            return StandardCalculator.VARIANCE;
        if (type == 12)
            return StandardCalculator.VARIANCE_P;
        return StandardCalculator.SUM;
    }

    @Override
    public Component getComponent() {
        return pivot;
    }

    @Override
    public void reload(Component widget) {
        if (model != null) {
            pivot.setModel(null);
            model = null;
            formats.clear();
        }
        redraw();
    }

    private DecimalFormat _fnf;

    private DecimalFormat _nnf;

    public String renderCell(Number number, Pivottable pivottable,
                             PivotHeaderContext pivotheadercontext,
                             PivotHeaderContext pivotheadercontext1, PivotField pivotfield) {
        if (number == null)
            return "";
        String format = formats.get(pivotfield.getFieldName());
        if (format != null)
            return new DecimalFormat(format).format(number);
        return (number instanceof Integer) ? _nnf.format(number) : _fnf
                .format(number);
    }

    public int getColumnSize(Pivottable pivottable,
                             PivotHeaderContext pivotheadercontext, PivotField pivotfield) {
        if (pivotfield != null) {
            if (pivotheadercontext.isGrandTotal()) {

            } else {
            }
        }
        return !pivotheadercontext.isGrandTotal() || pivotfield == null ? 100
                : 150;
    }

    public int getRowSize(Pivottable pivottable,
                          PivotHeaderContext pivotheadercontext, PivotField pivotfield) {
        if (pivotfield != null) {
            if (pivotheadercontext.isGrandTotal()) {

            } else {

            }
        }
        return 20;
    }

    public String renderField(Object obj, Pivottable pivottable,
                              PivotField pivotfield) {
        return pivotfield.getType() != org.zkoss.pivot.PivotField.Type.DATA ? obj != null ? String
                .valueOf(obj) : "(null)"
                : pivotfield.getTitle();
    }

    public String renderGrandTotalField(Pivottable pivottable,
                                        PivotField pivotfield) {
        if (pivotfield == null)
            return Labels.getLabel("pivot.grand.total");
        else
            return (new StringBuilder()).append(Labels.getLabel("pivot.grand.total.of"))
                    .append(pivotfield.getTitle()).toString();
    }

    public String renderSubtotalField(Object obj, Pivottable pivottable,
                                      PivotField pivotfield, Calculator calculator) {
        String s = calculator.getLabel();
        String s1 = obj != null ? obj.toString() : "Null";
        return (new StringBuilder()).append(s1).append(" ").append(s)
                .toString();
    }

    public String renderDataField(PivotField pivotfield) {
        return pivotfield.getFieldName();
    }

    public String renderCellSClass(Number number, Pivottable pivottable,
                                   PivotHeaderContext rowContext,
                                   PivotHeaderContext columnContext, PivotField pivotfield) {
        return null;
    }

    public String renderCellStyle(Number number, Pivottable pivottable,
                                  PivotHeaderContext rowContext,
                                  PivotHeaderContext columnContext, PivotField pivotfield) {
        if (scriptEval != null) {
            scope.clear();
            scope.setVariable(pivotfield.getFieldName(), number);
            Context cx = Context.enter();
            try {
                cx.setOptimizationLevel(9);
                Object obj = scriptEval.exec(cx, scope);
                if (obj instanceof String)
                    return (String) obj;
            } catch (RhinoException ex) {
                return null;
            } finally {
                Context.exit();
            }
        }
        return null;
    }

    @Override
    public void destory() {
        if (scriptEval != null) {
            scope.clear();
            scope = null;
            scriptEval = null;
        }
    }
}
